#include "audiomanager.h"
#include "baseapplication.h"
#include "mixchunk.h"

using std::string;
using std::list;
using std::map;
using std::pair;
using namespace EPOC;

EPOC::AudioManager *EPOC::g_pMixer = NULL;

void CallbackChannelDone(int nChannel)
{
	g_pMixer->NotifyChannelDone(nChannel);
}

AudioManager::AudioManager(BaseApplication *pApplication)
{
	g_pMixer = this;
	m_pApplication = pApplication;
	SetClassID(kCLASSID_AUDIOMANAGER);

	m_nFrequency = 0;
	m_nOutputChannels = 0;
	m_nSampleChunkSize = 0;
	m_nFormat = 0;
	m_bInitialized = false;

	m_nMasterVolume = SDL_MIX_MAXVOLUME;
	m_nMixingChannels = 16;

	
}

AudioManager::~AudioManager()
{
	AudioManager::Cleanup();
}


void AudioManager::Cleanup()
{
	for(map<string, Mix_Music*>::iterator it = m_rLoadedMusic.begin(); it != m_rLoadedMusic.end(); it++)
	{
		Mix_FreeMusic((*it).second);	
	}
	m_rLoadedMusic.clear();	

	for(list<Music*>::iterator it = m_rMusic.begin(); it != m_rMusic.end(); it++)
		delete (*it);
	m_rMusic.clear();

	for(list<Sound*>::iterator it = m_rSounds.begin(); it != m_rSounds.end(); it++)
		delete (*it);
	m_rSounds.clear();

	for(list<MixChunk*>::iterator it = m_rLoadedMixChunks.begin(); it != m_rLoadedMixChunks.end(); it++)
	{
		(*it)->Release();
	}
	m_rLoadedMixChunks.clear();


	Mix_CloseAudio();

	g_pMixer = NULL;
}


bool AudioManager::Initialize(int nMixingChannels, int nFrequency,  int nSampleChunkSize, Uint16 nFormat, int nOutputChannels)
{
	m_nMixingChannels = nMixingChannels;
	m_nFrequency = nFrequency;
	m_nFormat = nFormat;
	m_nOutputChannels = nOutputChannels;
	m_nSampleChunkSize = nSampleChunkSize;

	//44100
	if(Mix_OpenAudio(m_nFrequency, m_nFormat, m_nOutputChannels, m_nSampleChunkSize) < 0)
	{
		fprintf( stderr, "AudioManager: Error, unable to initialize audio mixer\n");
		return false;
	}

	m_bInitialized = true;

	Mix_AllocateChannels(m_nMixingChannels);
	Mix_ChannelFinished(CallbackChannelDone);

	return true;
}

void AudioManager::NotifyChannelDone(int nChannel)
{
	for(list<Sound*>::iterator it = m_rSounds.begin(); it != m_rSounds.end(); it++)
		(*it)->NotifyChannelDone(nChannel);
}

bool AudioManager::Update(float fTimeDelta)
{
	for(list<Music*>::iterator it = m_rMusic.begin(); it != m_rMusic.end();)
	{
		if((*it)->Update(fTimeDelta) == false)
		{
			Music *pMusic = (*it);
			it = m_rMusic.erase(it);
			delete pMusic;
		}
		else
			 it++;
	}

	for(list<Sound*>::iterator it = m_rSounds.begin(); it != m_rSounds.end();)
	{
		if((*it)->Update(fTimeDelta) == false)
		{
			Sound *pSound = (*it);
			it = m_rSounds.erase(it);
			pSound->Release();
		}
		else
			 it++;
	}


	return true;
}



Music* AudioManager::CreateMusic(string sFilePath, int nVolume)
{
	Music *pMusic = new Music(this);
	if(pMusic->Load(sFilePath) == false)
	{
		delete pMusic;
		pMusic = NULL;
	}
	else
	{
		pMusic->SetVolume(nVolume);
		m_rMusic.push_back(pMusic);
	}

	return pMusic;
}

Mix_Music* AudioManager::LoadMusicFromFile(string sFilePath)
{
	Mix_Music *pMusic = GetLoadedMusic(sFilePath);
	if(pMusic == NULL)
	{
		pMusic = Mix_LoadMUS(sFilePath.c_str());
	
		if(pMusic)
		{
			pair<string, Mix_Music*> entry;
			entry.first = sFilePath;
			entry.second = pMusic;

			m_rLoadedMusic.insert(entry);
		}
	}

	return pMusic;
}


Mix_Music* AudioManager::GetLoadedMusic(string sFilePath)
{
	Mix_Music *pMusic = NULL;

	if(!m_rLoadedMusic.empty())
	{
		for(map<string, Mix_Music*>::iterator it = m_rLoadedMusic.begin(); it != m_rLoadedMusic.end(); it++)
		{
			if((*it).first.compare(sFilePath) == 0)
			{
				pMusic = (*it).second;
				break;
			}
		}
	}

	return pMusic;
}

Sound* AudioManager::CreateSound(string sFilePath, int nVolume)
{
	Sound *pSound = new Sound(this);
	if(pSound->Load(sFilePath) == false)
	{
		pSound->Release();
		pSound = NULL;
	}
	else
	{
		pSound->SetVolume(nVolume);
		AddSoundObject(pSound);
		//m_rSounds.push_back(pSound);
	}

	return pSound;
}

void AudioManager::DestroySound(Sound *pSound)
{
	if(!pSound)
		return;

	//fprintf(stdout, "Debug - AudioManager::DestroySound:\n");

	if(pSound->GetRefCount() <= 1)
	{
		//fprintf(stdout, "Debug - AudioManager::DestroySound: Removed!\n");
		RemoveSoundObject(pSound);
	}

	//RemoveRenderObject(pImage);

	pSound->Release();
	pSound = NULL;
}
								  

void AudioManager::AddSoundObject(Sound *pObject)
{
	if(!pObject)
		return;

	m_rSounds.push_back(pObject);
}

void AudioManager::RemoveSoundObject(Sound *pObject)
{
	if(!pObject)
		return;

	//fprintf(stdout, "Debug - AudioManager::RemoveSoundObject:\n");

	m_rSounds.remove(pObject);
}


MixChunk* AudioManager::LoadMixChunkFromFile(string sFilePath)
{
	MixChunk *pMixChunk = NULL;

	pMixChunk = GetLoadedMixChunk(sFilePath);
	if(!pMixChunk)
	{
		Mix_Chunk *pSDLMixChunk = Mix_LoadWAV(sFilePath.c_str());
	
		if(pSDLMixChunk)
		{
			pMixChunk = new MixChunk(this);
			if(pMixChunk->Initialize(sFilePath, pSDLMixChunk) == false)
			{
				pMixChunk->Release();
				pMixChunk = NULL;
			}
			else
			{
				m_rLoadedMixChunks.push_back(pMixChunk);
			}
		}
	}
	else
	{
		pMixChunk->AddRef();
	}

	return pMixChunk;
}

MixChunk* AudioManager::GetLoadedMixChunk(string sFilePath)
{
	MixChunk *pMixChunk = NULL;
	//fprintf(stdout, "Debug - AudioManager::GetLoadedSound: Searching for MixChunk \n");

	if(!m_rLoadedMixChunks.empty())
	{

		//for(map<string, Mix_Chunk*>::iterator it = m_rLoadedMixChunks.begin(); it != m_rLoadedMixChunks.end(); it++)
		for(list<MixChunk*>::iterator it = m_rLoadedMixChunks.begin(); it != m_rLoadedMixChunks.end(); it++)
		{
			if((*it)->GetFilePath().compare(sFilePath) == 0)
			{
				pMixChunk = (*it);
				//fprintf(stdout, "Debug - AudioManager::GetLoadedSound:  ... Found!\n");
				break;
			}
		}
	}

	return pMixChunk;
}


void AudioManager::RemoveLoadedMixChunk(MixChunk *pMixChunk)
{
	if(!pMixChunk)
		return;

	//fprintf(stdout, "Debug - EpGraphicsManager::RemoveLoadedSound: Searching for MixChunk to remove from container: \n");

	if(!m_rLoadedMixChunks.empty())
	{
		//fprintf(stdout, "Debug - EpGraphicsManager::RemoveLoadedSound: Removing  \n");
		m_rLoadedMixChunks.remove(pMixChunk);
	}
}